#include "StdAfx.h"
#include "Magic.h"
#include "I3DEngine.h"

#include "Game.h" // diz
#include "WeaponSystem.h"
#include "Player.h"
#include "C4Projectile.h"
#include "Rocket.h"
#include "ParticleParams.h"
#include "Item.h"
#include "Weapon.h"
#include "GameParameters.h"
#include "ItemResourceCache.h"
#include "Player.h"
#include "Projectile.h"

class CMagic : public CWeapon
{
private:
	struct SMagic
	{
		float fRequiredMana;
		string sEffectType;
		// the cooldown maybe? hm k
		CTimeValue pCooldown;
		float fEffect; // what will be effect? effect on healths, ok
		Vec3 vVelocity;
	};

	typedef std::map<string, SMagic*> TMapMagic;
	TMapMagic m_mapMagic;

	void SpawnProjectile(string sClassName, string sParticle);

public:
	CMagic();
	virtual ~CMagic();

	virtual void Select(bool select); // 
};

// STILL WIP
class BE_EECleaner : IEntityEventListener
{
private:
	IEntity * pT;
public:
	BE_EECleaner(IEntity * pTarget, int iRT)
	{
		pT = pTarget;
		pTarget->SetTimer(pTarget->GetId(), iRT);
		gEnv->pEntitySystem->AddEntityEventListener( pT->GetId(),ENTITY_EVENT_TIMER,this);
	}
	~BE_EECleaner(){};

	virtual void OnEntityEvent( IEntity *pEntity,SEntityEvent &event )
	{
		if (event.event == ENTITY_EVENT_TIMER && event.nParam[0] == pT->GetId())
		{
			gEnv->pEntitySystem->RemoveEntity(pT->GetId());
			this->~BE_EECleaner();
		}
	}
};

class BE_EE : IEntityEventListener
{
	private:
		IEntity * pSource;
		float fSize;
		int iTimer, iDD;
		bool bExpl, bAlExp;
		char * pAfterEffect;
		int iCFlag;
		IParticleEmitter * pPE;
		int iTimerID;
		pe_action_impulse impulse;
		pe_status_pos spos;
		pe_status_dynamics sdyn;
		SEntityProximityQuery query;
		AABB ab1;

	public:
		BE_EE(IEntity * p1, char * pAfterEffect = NULL, float fAreaScan = 0.5, int iDirectDamage = 500, bool bExplosion=false)
		{
			iTimer = 15;
			bAlExp = false;
			iCFlag = 0x0;
			pPE = NULL;

			pSource = p1;
			this->pAfterEffect = pAfterEffect;
			fSize =  fAreaScan;
			iDD = iDirectDamage;
			bExpl = bExplosion;

			gEnv->pEntitySystem->AddEntityEventListener(pSource->GetId(), ENTITY_EVENT_COLLISION, this);
			gEnv->pEntitySystem->AddEntityEventListener(pSource->GetId(),ENTITY_EVENT_TIMER,this);
			gEnv->pEntitySystem->AddEntityEventListener(pSource->GetId(), ENTITY_EVENT_DONE, this);
			iTimerID = pSource->GetId() + 550055001;
			pSource->SetTimer(iTimerID, iTimer);
		}
		~BE_EE()
		{
// 			if (pPE!=NULL)
// 				gEnv->p3DEngine->DeleteParticleEmitter(pPE);

			pSource->KillTimer(iTimerID);

			gEnv->pEntitySystem->RemoveEntityEventListener(pSource->GetId(), ENTITY_EVENT_DONE, this);
			gEnv->pEntitySystem->RemoveEntityEventListener(pSource->GetId(), ENTITY_EVENT_COLLISION, this);
			gEnv->pEntitySystem->RemoveEntityEventListener( pSource->GetId(),ENTITY_EVENT_TIMER,this);
		}

		void addEffect(IEntity * pEntity)
		{
			if(pEntity && pAfterEffect)
			{
				IParticleEffect *pParticleEffect = NULL;
				pParticleEffect = gEnv->pParticleManager->FindEffect(pAfterEffect);
				int i=0;
				SEntitySlotInfo dummy;
				while (pEntity->GetSlotInfo(++(i), dummy));
				Vec3 offset(0,0,0.2f); Vec3 dir(0,0,1); float scale = 1.5f;
				pEntity->LoadParticleEmitter(i, pParticleEffect, 0, true/*prime*/, true);
				pEntity->SetSlotLocalTM(i, Matrix34(IParticleEffect::ParticleLoc(offset, dir, scale)));
				new BE_EECleaner(pEntity, 10000);
			}
		}

		void addDirectDamage(IEntity * pEntity, int iDirectDamage = 0x0)
		{
			HitInfo hitInfo;
			hitInfo.shooterId= pSource->GetId();
			hitInfo.targetId = pEntity->GetId();
			hitInfo.weaponId = pSource->GetId();
			hitInfo.damage = float(iDirectDamage == 0x0 ? iDD : iDirectDamage); //from int to float
			hitInfo.pos = pEntity->GetPos();
			hitInfo.type = gEnv->pGame->GetIGameFramework()->GetIGameRulesSystem()->GetCurrentGameRules()->GetHitTypeId("fire");
			gEnv->pGame->GetIGameFramework()->GetIGameRulesSystem()->GetCurrentGameRules()->ServerHit(hitInfo);
		}

		void addImpulse(IEntity * pEntity)
		{
			if(pEntity->GetPhysics())
			{
				impulse.iApplyTime = 0;
// 				impulse.impulse = Vec3(pSource->GetRotation()*Vec3(0,1,0))*20+Vec3(0,0,5);
// 				impulse.impulse *= 40;
				impulse.impulse = pEntity->GetPos() - pSource->GetPos();		
				impulse.impulse.Normalize();
				if(impulse.impulse.x==0 || impulse.impulse.y==0 || impulse.impulse.z==0 )
				{
					impulse.impulse = Vec3(pSource->GetRotation()*Vec3(0,1,0))*20+Vec3(0,0,5);
					impulse.impulse.Normalize();
				}
				else
				{
					impulse.impulse.x *= 10.0f;
					impulse.impulse.y *= 10.0f;
				}
				impulse.impulse.z = (impulse.impulse.z < 0 ? impulse.impulse.z * -1 : 0x0);
				impulse.impulse *= 50;
				pEntity->GetPhysics()->Action(&impulse, true);
			}
		}

		void addExplosion(void)
		{
			{//Spawn the ExplosionEffect
				IParticleEffect *pParticleEffect = NULL;
				pParticleEffect = gEnv->pParticleManager->FindEffect("explosions.rocket_terrain.explosion");
				Matrix34 m34 = pSource->GetWorldTM();
				m34.ScaleColumn(Vec3(0.08f));
				pPE = gEnv->pParticleManager->CreateEmitter(m34 , pParticleEffect->GetParticleParams());	
				pPE->SetEffect(pParticleEffect);
				pPE->Activate(true);
			}//

			pe_explosion explosion;
			{//
				explosion.epicenter = pSource->GetPos();
				explosion.r = explosion.rmin = explosion.rmax = 5.0f;
			}//

			IEntity * pEntity = NULL;
			IPhysicalEntity * pPhEnt = NULL;
			Vec3 delta(0.0f);

			//skip for dude dont work... :-/
			gEnv->pPhysicalWorld->SimulateExplosion( &explosion, 0x0, 0x0, ent_all);
			for (int i=0; i< explosion.nAffectedEnts; ++i)
			{ 
				pPhEnt = NULL;	
				if (	(pEntity = gEnv->pEntitySystem->GetEntityFromPhysics(explosion.pAffectedEnts[i])) 
					&&	(pPhEnt = pEntity->GetPhysics()) 
					&&	(pEntity != gEnv->pGame->GetIGameFramework()->GetClientActor()->GetEntity()) 
					)
				{
					if (!pPhEnt->GetStatus(&spos) || !pPhEnt->GetStatus(&sdyn))
						continue;

					delta = spos.pos - pSource->GetPos();		
					delta.Normalize();
					if(delta.x==0 || delta.y==0 || delta.z==0 )
					{
						delta = Vec3(pSource->GetRotation()*Vec3(0,1,0))*20+Vec3(0,0,5);
						delta.Normalize();
					}

					{//
						float fExpand = (explosion.r+(sdyn.mass/10.0f))*30;
						impulse.impulse = delta*fExpand;
						impulse.impulse.z = (impulse.impulse.z < 0 ? impulse.impulse.z * -1 : 0x0);
						impulse.iApplyTime = 0;
						pPhEnt->Action(&impulse);
					}//

					addEffect(pEntity);
					addDirectDamage(pEntity, iDD);
					
				}
			}
		}

		virtual void OnEntityEvent( IEntity *pEntity,SEntityEvent &event )
		{
			if (event.event == ENTITY_EVENT_TIMER)
			{

				if(event.nParam[0]==iTimerID)
					pSource->SetTimer(event.nParam[0], 5);

				IEntity* pEntity = 0x0;

				pSource->GetWorldBounds(ab1);
				query.box = AABB(ab1.GetCenter()-Vec3(fSize,fSize,fSize), ab1.GetCenter()+Vec3(fSize,fSize,fSize));
				gEnv->pEntitySystem->QueryProximity(query);

				for (int i = 0; i < query.nCount; ++i)
				{				
					if ((pEntity = query.pEntities[i]) && pEntity->GetPhysics())
					{
						if(pEntity->GetId()!=g_pGame->GetIGameFramework()->GetClientActor()->GetEntity()->GetId() && pEntity->GetId() != pSource->GetId())
						{
							pEntity->GetWorldBounds(ab1);
							Vec3 v3(ab1.GetCenter());
							pSource->GetWorldBounds(ab1);
							Vec3 v31(ab1.GetCenter());

							if(pEntity->GetPhysics()->GetType()==PE_LIVING || pEntity->GetPhysics()->GetType()==PE_WHEELEDVEHICLE)
								if( (v3.z+fSize*3) >= v31.z && (v3.z-fSize*3) <= v31.z)
								{
									CProjectile * pP = static_cast<CProjectile*>(g_pGame->GetWeaponSystem()->GetProjectile(pSource->GetId()));
									if (pP){pP->Destroy();}

									if (bExpl)
									{
										addExplosion();
										break;
									}
									else
									{
										addImpulse(pEntity);
										addEffect(pEntity);
										addDirectDamage(pEntity);
									}

								}//if( (v3.z+fSize*2) >= v31.z && (v3.z-fSize*2) <= v31.z)
						}//if(pEntity->GetId()!=g_pGame->GetIGameFramework()->GetClientActor()->GetEntity()->GetId() && pEntity->GetId() != pSource->GetId())
					}//if ((pEntity = query.pEntities[i]) && pEntity->GetPhysics())
				}//for (int i = 0; i < query.nCount; ++i)



			}
			else if(event.event == ENTITY_EVENT_COLLISION)
			{
				EventPhysCollision *pCollision = (EventPhysCollision *)event.nParam[0];
				IEntity* pTarget = pCollision->iForeignData[1]==PHYS_FOREIGN_ID_ENTITY ? (IEntity*)pCollision->pForeignData[1] : 0;

				IEntity* pEntity = 0x0;

				pSource->GetWorldBounds(ab1);
				query.box = AABB(ab1.GetCenter()-Vec3(fSize,fSize,fSize), ab1.GetCenter()+Vec3(fSize,fSize,fSize));
				gEnv->pEntitySystem->QueryProximity(query);

				for (int i = 0; i < query.nCount; ++i)
				{				
					if ((pEntity = query.pEntities[i]) && pEntity->GetPhysics())
					{
						if(pEntity->GetId()!=g_pGame->GetIGameFramework()->GetClientActor()->GetEntity()->GetId() && pEntity->GetId() != pSource->GetId())
						{
							pEntity->GetWorldBounds(ab1);
							Vec3 v3(ab1.GetCenter());
							pSource->GetWorldBounds(ab1);
							Vec3 v31(ab1.GetCenter());

							if(pEntity->GetPhysics()->GetType()==PE_LIVING || pEntity->GetPhysics()->GetType()==PE_WHEELEDVEHICLE)
								if( (v3.z+fSize*3) >= v31.z && (v3.z-fSize*3) <= v31.z)
								{
									CProjectile * pP = static_cast<CProjectile*>(g_pGame->GetWeaponSystem()->GetProjectile(pSource->GetId()));
										if (pP){pP->Destroy();}

									if (bExpl)
									{
										addExplosion();
										break;
									}
									else
									{
										addImpulse(pEntity);
										addEffect(pEntity);
										addDirectDamage(pEntity);
									}

									

								}//if( (v3.z+fSize*2) >= v31.z && (v3.z-fSize*2) <= v31.z)
						}//if(pEntity->GetId()!=g_pGame->GetIGameFramework()->GetClientActor()->GetEntity()->GetId() && pEntity->GetId() != pSource->GetId())
					}//if ((pEntity = query.pEntities[i]) && pEntity->GetPhysics())
				}//for (int i = 0; i < query.nCount; ++i)

				if(pTarget)
				{
					CProjectile * pP = static_cast<CProjectile*>(g_pGame->GetWeaponSystem()->GetProjectile(pSource->GetId()));
						if (pP){pP->Destroy();}

					if (bExpl)
					{
						addExplosion();
						bExpl = false;
					}
					else
					{
						addImpulse(pTarget);
						addEffect(pTarget);
						addDirectDamage(pTarget);
					}
					
				}//if(pTarget)

				if (bExpl)
					addExplosion();

			}//else if(event.event == ENTITY_EVENT_COLLISION)
			else if(event.event == ENTITY_EVENT_DONE)
			{
				int i = event.nParam[0];
				if (i == pSource->GetId())
				{
					this->~BE_EE();
				}
			}

		}//virtual void OnEntityEvent( IEntity *pEntity,SEntityEvent &event )

};
// end of da effect blaster shiz


CMagic::CMagic()
{
	SMagic* pMagic = new SMagic();

	pMagic->sEffectType = "woosh";
	pMagic->pCooldown = CTimeValue(15.0f);
	pMagic->fEffect = 50.f;
	pMagic->fRequiredMana = 25 ;
	pMagic->vVelocity(2,2,2);

	m_mapMagic["testMagic"] = pMagic; 
}

CMagic::~CMagic()
{

}

void CMagic::Select(bool select)
{
	CWeapon::Select(select);
} // projectile yes?, there is SpawnParticleEffect, not projectile oh lol, my curiosity

void CMagic::SpawnProjectile(string sClassName, string sParticle)
{
            IEntityClass *pClass = gEnv->pEntitySystem->GetClassRegistry()->FindClass(sClassName);

            //The next lines are to cache the trail effect for rockets
            CItemParticleEffectCache& particleCache = g_pGame->GetGameSharedParametersStorage()->GetItemResourceCache().GetParticleEffectCache();
            //Edit the xml files in GameSDK\Scripts\Entities\Items\XML\Ammo
            //To find the strings for these effects - e.g. Rocket.xml
            //See 5 posts down if you want code to read
            //this effect name from the Rocket.xml file

            particleCache.CacheParticle("weapon_fx.rocketlauncher.tracer.tracer"); // we will insert our particle here


            if(!pClass)
            {
               return;
            }

            CProjectile* proj = g_pGame->GetWeaponSystem()->SpawnAmmo(pClass, true);
            
            if(!proj)
            {
               return;
            }
            
			CPlayer *pPlayer = static_cast<CPlayer*>(g_pGame->GetIGameFramework()->GetClientActor());

			Vec3  pos = pPlayer->GetEntity()->GetPos();
			Vec3  dir = Vec3(0,0,0); // direct at the player!
			Vec3  vel = Vec3(4,4,4); // i dunno lol , i never used 'map' :D :D
            proj->Launch(pos, dir, vel);
}